package com.dwf.dominion;

import java.util.Random;
import java.util.Vector;

import com.dwf.dominion.Card.Category;

import android.util.Log;

public class RuleList {
	public enum RuleListType {FIXED_WEIGHT,ADAPTIVE_WEIGHT,RANDOM};
	
	private String name;
	private RuleListType type;
	private Vector<Card> tempReqCards;
	private Vector<Card> tempBanCards;
	private Vector<Card> permReqCards;
	private Vector<Card> permBanCards;
	private Vector<Rule> rules;
	
	public RuleList(String name, RuleListType type)
	{
		this.name = name;
		this.type = type;
		this.permReqCards = new Vector<Card>();
		this.permBanCards = new Vector<Card>();
		this.rules = new Vector<Rule>();
		resetTempLists();
	}
	
	private void resetTempLists()
	{
		this.tempReqCards = new Vector<Card>();
		this.tempReqCards.addAll(permReqCards);
		this.tempBanCards = new Vector<Card>();
		this.tempBanCards.addAll(permBanCards);
		validateRuleConditions (this.tempReqCards, this.rules);
	}
	
	private void validateRuleConditions(Vector<Card> cardList, Vector<Rule> ruleList)
	{
		for (Card c: cardList)
		{
			for (Rule r: ruleList)
			{
				if (r.satisfiesCondition(c))
				{
					r.conditionCount++;
				}
			}
		}
		
		for (Rule r: ruleList)
		{
			if ((r.conditionCategory == Category.NONE) ||
					((r.conditionCount > r.conditionMinimum) && (r.conditionCount < r.conditionMaximum)))
				r.conditionSatisfied = true;
			else
				r.conditionSatisfied = false;
		}
		
		return;
	}
	
	private boolean validateRuleMinimums (Vector<Card> cardList, Vector<Rule> ruleList)
	{					
		for (Card c: cardList)
		{
			for (Rule r: ruleList)
			{
				if (r.satisfiesRequirement(c))
				{
					r.requirementCount++;
				}
			}
		}
		
		for (Rule r: ruleList)
		{
			if (r.requirementCount > r.requiredMinimum)
			{
				r.requirementSatisfied = true;
			}
			else
				r.requirementSatisfied = false;
		}
		
		for (Rule r : ruleList)
		{
			if (r.conditionSatisfied)
			{
				if (!r.requirementSatisfied)
					return false;
			}
		}
		
		return true;
	}
	
	private void validateMaximums (Vector<Rule> ruleList, Vector<Card> currentCardList, Vector<Card> fullCardList)
	{
		int test;
		
		for (Rule r: ruleList)
		{
			if (r.requiredCategory == Category.SPECIFIC_CARD)
				continue;
			test = 0;
			for (Card c: currentCardList)
			{
				if (r.satisfiesRequirement(c))
				{
					test++;
				}
				if (test >= r.requiredMaximum)
				{
					removeCategoryElements (currentCardList, r.requiredCategory);
				}
			}
		}
	}

	private boolean validateMaximums (Vector<Rule> ruleList, Vector<Card> currentCardList)
	{
		int test;
		
		for (Rule r: ruleList)
		{
			if (r.requiredCategory == Category.SPECIFIC_CARD)
				continue;
			test = 0;
			for (Card c: currentCardList)
			{
				if (r.satisfiesRequirement(c))
				{
					test++;
				}
				if (test > r.requiredMaximum)
				{
					return false;
				}
			}
		}
		return true;
	}
	
	private void removeCategoryElements (Vector<Card> cardList, Category cat)
	{
		Vector<Card> tempCopy = new Vector<Card>();
		tempCopy.addAll(cardList);
		
		for (Card c: tempCopy)
		{
			if (c.isCategory(cat))
			{
				cardList.remove(c);
			}
		}
	}
	
	public String getName()
	{
		return name;
	}
		
	public boolean addTempRequiredCard (Card c)
	{
		if ((this.getLockedSlots(rules) + this.tempReqCards.size() + 1) > 10)
			return false;

		//Don't want to add it to the list twice
		if(tempReqCards.contains(c) == false)
		{
			tempReqCards.add(c);
		}
		
		for (Card bCard : tempBanCards)
		{
			if (bCard.equals(c))
			{
				tempBanCards.remove(bCard);
				break;
			}
		}
				
		return true;
	}
	
	public void addTempBannedCard (Card c)
	{
		//Don't want to add it twice
		if(tempBanCards.contains(c) == false) 
		{
			tempBanCards.add(c);
		}

		for (Card rCard : tempReqCards)
		{
			if (rCard.equals(c))
			{
				tempReqCards.remove(rCard);
				break;
			}
		}		
	}
	
	public void removeTempRequiredCard (Card c)
	{
		for (Card card: tempReqCards)
		{
			if (card.equals(c))
			{
				tempReqCards.remove(card);
				break;
			}
		}
	}
	
	public void removeTempBannedCard (Card c)
	{
		for (Card card: tempBanCards)
		{
			if (card.equals(c))
			{
				tempBanCards.remove(card);
				break;
			}
		}
	}

	public boolean addPermRequiredCard (Card c)
	{
		if (permReqCards.size() == 10)
			return false;
		
		//Don't want to add it to the list twice
		if(permReqCards.contains(c) == false)
		{
			permReqCards.add(c);
		}
		
		validateRuleConditions (this.permReqCards, this.rules);

		if (this.getLockedSlots(rules) > 10)
		{
			permReqCards.remove(c);
			return false;
		}

		if (!validateMaximums (this.rules, this.permReqCards))
		{
			permReqCards.remove(c);
			return false;
		}

		for (Card card: permBanCards)
		{
			if (card.equals(c))
			{
				permBanCards.remove(card);
				break;
			}
		}

		resetTempLists();
		
		return true;
	}

	
	public void addPermBannedCard (Card c)
	{
		//Don't want to add it twice
		if(permBanCards.contains(c) == false) 
		{
			permBanCards.add(c);
		}
		
		for (Card card: permReqCards)
		{
			if (card.equals(c))
			{
				permReqCards.remove(card);
				break;
			}
		}
		
		resetTempLists();
	}
	
	public void removePermRequiredCard (Card c)
	{
		for (Card card: permReqCards)
		{
			if (card.equals(c))
			{
				permReqCards.remove(card);
				break;
			}
		}
		
		resetTempLists();
	}
	
	public void removePermBannedCard (Card c)
	{
		for (Card card: permBanCards)
		{
			if (card.equals(c))
			{
				permBanCards.remove(card);
				break;
			}
		}

		resetTempLists();
	}

	public boolean addRule (Rule r)
	{
		if((permReqCards.size() + getLockedSlots (this.rules) + r.requiredMinimum) > 10)
			return false;
		
		this.rules.add(r);
		
		validateRuleConditions (this.permReqCards, this.rules);

		if (this.getLockedSlots(this.rules) > 10)
		{
			rules.remove(r);
			return false;
		}
		
		if (!validateMaximums (this.rules, this.permReqCards))
		{
			rules.remove(r);
			return false;
		}

		resetTempLists();
		
		return true;
	}
	
	private int getLockedSlots (Vector<Rule> rules)
	{
		int result = 0;
		
		for (Rule r: rules)
		{
			if (r.requiredMinimum - r.requirementCount > 0)
				result += r.requiredMinimum - r.requirementCount;
		}
		
		return result;
	}
	
	public Vector<Card> getNewList(Vector<Card> currentCardList)
	{
		Vector<Card> resultList;
		Random rand = new Random();

		Cards list_of_possible_cards = new Cards();
				
		for (Card c: tempBanCards)
		{
			//TODO: bug here.  Not removing cards
			for (Card card : list_of_possible_cards.cards)
			{
				if (card.equals(c))
				{
					list_of_possible_cards.cards.remove(card);
					break;
				}
			}
		}
		
		if(currentCardList == null) {
			resultList= new Vector<Card>();

			for (Card c: tempReqCards)
			{
				for (Card card : list_of_possible_cards.cards)
				{
					if (card.equals(c))
					{
						list_of_possible_cards.cards.remove(card);
						break;
					}					
				}
				c.state = Card.State.TEMP_LOCK;
				resultList.add(c);
			}
		}
		else
		{
			resultList = currentCardList;
			
			//Remove all of the cards in the current list
			for (Card c: resultList) {
				for (Card card: list_of_possible_cards.cards)
				{
					if (card.equals(c))
					{
						list_of_possible_cards.cards.remove(card);
						break;
					}
				}
			}
		}
		
		while (resultList.size() < 10)
		{
			validateRuleConditions (resultList, this.rules);
			validateMaximums (this.rules, resultList, list_of_possible_cards.cards);
			validateRuleMinimums (resultList, this.rules);
			if (list_of_possible_cards.cards.size() < 10 - resultList.size())
				return resultList;
						
			// Loop until all of the unlocked slots are full
			while (resultList.size() < (10 - this.getLockedSlots(this.rules)))
			{
				int card_location = rand.nextInt(list_of_possible_cards.cards.size());
				Card c = list_of_possible_cards.cards.elementAt(card_location);
				resultList.add(list_of_possible_cards.cards.elementAt(card_location));
				list_of_possible_cards.cards.remove(card_location);
				validateRuleConditions (resultList, this.rules);
				validateMaximums (this.rules, resultList, list_of_possible_cards.cards);
				validateRuleMinimums (resultList, this.rules);				
				if (list_of_possible_cards.cards.size() < 10 - resultList.size())
					return resultList;
			}
			for (Rule r: this.rules)
			{
				if (r.conditionSatisfied)
				{
					if (!r.requirementSatisfied)
					{
						Vector<Card> categoryCards = list_of_possible_cards.getCategory (r.requiredCategory);
						int card_location = rand.nextInt(categoryCards.size());
						Card c = categoryCards.elementAt(card_location);
						resultList.add(categoryCards.elementAt(card_location));
						//TODO make sure this remove method works
						// I think that c is the same reference as the one in list_of_...
						list_of_possible_cards.cards.remove(c);
						validateRuleConditions (resultList, this.rules);
						validateMaximums (this.rules, resultList, list_of_possible_cards.cards);
						validateRuleMinimums (resultList, this.rules);		
						if (list_of_possible_cards.cards.size() < 10 - resultList.size())
							return resultList;
					}
				}
				break;
			}
			if (resultList.size() + this.getLockedSlots(this.rules) == 10)
			{
				// This condition will occur if there are still locked slots but there are no slots
				// available for new cards.  In this case we will assume that the condition will never
				// be met and unlock the slots.
				for (Rule r: this.rules)
				{
					if (!r.conditionSatisfied)
					{
						if (!r.requirementSatisfied)
						{
							// Invalidate this rule; it will never be considered satisfied
							r.requirementSatisfied = true;
							r.requirementCount = r.requiredMinimum;
							removeCategoryElements (list_of_possible_cards.cards, r.conditionCategory);
							if (list_of_possible_cards.cards.size() < 10 - resultList.size())
								return resultList;
							break;
						}
					}
				}
			}

		}
/*		while(resultList.size()<10)
		{
			int card_location = rand.nextInt(list_of_possible_cards.cards.size());
			Card c = list_of_possible_cards.cards.elementAt(card_location);
			resultList.add(list_of_possible_cards.cards.elementAt(card_location));
			list_of_possible_cards.cards.remove(card_location);
		}
	*/	
		sortCardsByName(resultList);
		
		return resultList;
	}
	
	public Vector<Card> sortCardsByName(Vector<Card> cards) {
		int i;
		int j;
		Card card1;
		Card card2;
		
		for(i=0;i<cards.size();i++) {
			card1 = cards.get(i);
			for(j=0;j<i;j++) {
				card2 = cards.get(j);
				if(card1.name.compareTo(card2.name) < 0) {
					cards.remove(i);
					cards.add(j, card1);
					//break the loop
					break;
				}
			}
		}
		return cards;
	}
	
	public Card replaceCard(Vector<Card> current_list, Card card_to_replace)
	{
		Cards list_of_possible_cards = new Cards();
		
		return list_of_possible_cards.cards.elementAt(0);
	}
}
